/*
 * Copyright 2019 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package io.netty.util.internal;

import static io.netty.util.internal.ObjectUtil.checkPositive;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

import io.netty.util.IllegalReferenceCountException;
import io.netty.util.ReferenceCounted;
//具体引用计数怎么实现的，主要是这个类。
// 但是他并不是用普通的那种引用一次计数器加1，释放一次减1，而是用了奇数和偶数，
// 如果还存在引用那么引用数是偶数，否则是奇数。
// 同时引用一次会加2，释放一次也减2，获取真实的计数是引用计数无符号右移1位，
// 看起来好像很奇怪，不过基本都是位操作和直接比较操作性能应该会提高点。
// 比如我们初始的时候真实引用计数=1，但是内部引用计数=2。如果有一次释放就内部引用计数-2，
// 两次就内部引用计数-4，当然引用的时候也一样，你会发现只要有引用，内部引用计数值就是偶数。
// 举个例子，我引用了3次，内部引用计数=6，获取真实引用计数刚好6>>>1=3,如果释放了3次，前2次会将内部引用计数=2，
// 但是最后一次如果发现内部引用计数=2的话，就会设置成1，这样内部引用计数刚好是奇数，真实引用计数刚好是1>>>1=0,就可以释放内存了。
public abstract class ReferenceCountUpdater<T extends ReferenceCounted> {


    protected ReferenceCountUpdater() { }

    public static long getUnsafeOffset(Class<? extends ReferenceCounted> clz, String fieldName) {
        try {
            if (PlatformDependent.hasUnsafe()) {
                return PlatformDependent.objectFieldOffset(clz.getDeclaredField(fieldName));
            }
        } catch (Throwable ignore) {
            // fall-back
        }
        return -1;
    }

    protected abstract AtomicIntegerFieldUpdater<T> updater();

    protected abstract long unsafeOffset();

    public final int initialValue() {
        return 2;
    }

    //这个就是获取真实的计数，是引用计数>>>1，同时前面会判断引用计数是否是偶数
    //偶数才有引用，奇数就直接返回0了。这里开始并不是直接用&判断奇数还是偶数，
    //而是直接用是否等于，这个比位操作更快。可见Netty在这性能方法真的做到了极致的提升。
    //因为大部分的计数可能就是1或者2
    //所以前面两个只要直接判断相等即可。
    private static int realRefCnt(int rawCnt) {
        return rawCnt != 2 && rawCnt != 4 && (rawCnt & 1) != 0 ? 0 : rawCnt >>> 1;
    }

    //这个主要是在释放的时候内部用的，如果真实计数已经是0，在释放就会报错，避免重复释放。
    private static int toLiveRealRefCnt(int rawCnt, int decrement) {
        //偶数就无符号右移一位。
        if (rawCnt == 2 || rawCnt == 4 || (rawCnt & 1) == 0) {
            return rawCnt >>> 1;
        }
        //奇数说明就已经释放了
        // odd rawCnt => already deallocated
        throw new IllegalReferenceCountException(0, -decrement);
    }

    //这个可以获取内部的引用计数，不是真实的。
    private int nonVolatileRawCnt(T instance) {
        // TODO: Once we compile against later versions of Java we can replace the Unsafe usage here by varhandles.
        final long offset = unsafeOffset();
        return offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);
    }

    //获取真实计数但是不会抛出异常
    public final int refCnt(T instance) {
        return realRefCnt(updater().get(instance));
    }

    //这个就是判断是否还存在引用，即内部的引用是否是偶数，是的话表示还有引用计数，返回true，不是就表示释放了，返回false。
    //最后也是先判断是否相等来优化。
    public final boolean isLiveNonVolatile(T instance) {
        final long offset = unsafeOffset();
        final int rawCnt = offset != -1 ? PlatformDependent.getInt(instance, offset) : updater().get(instance);

        // The "real" ref count is > 0 if the rawCnt is even.
        return rawCnt == 2 || rawCnt == 4 || rawCnt == 6 || rawCnt == 8 || (rawCnt & 1) == 0;
    }

    //直接设置真实引用计数，可以看到如果正数就会乘以2 ，负数就直接是1 也就是说没有设置成功，但是这里要注意 refCnt << 1 可能会是负数，溢出了，
    //eg: 1173741824<<1 =-1947483648
    public final void setRefCnt(T instance, int refCnt) {
        updater().set(instance, refCnt > 0 ? refCnt << 1 : 1); // overflow OK here
    }

    /**
     * Resets the reference count to 1
     */
    public final void resetRefCnt(T instance) {
        updater().set(instance, initialValue());
    }

    //真实引用计数+1 即引用计数+2 。
    public final T retain(T instance) {
        return retain0(instance, 1, 2);
    }

    //increment>0才可以，但是rawIncrement可能为负数（溢出了），这个后面会处理。
    public final T retain(T instance, int increment) {
        // all changes to the raw count are 2x the "real" change - overflow is OK
        int rawIncrement = checkPositive(increment, "increment") << 1;
        return retain0(instance, increment, rawIncrement);
    }

    // rawIncrement == increment << 1 增量 = 真实增量 * 2
    private T retain0(T instance, final int increment, final int rawIncrement) {
        int oldRef = updater().getAndAdd(instance, rawIncrement);
        //如果老的是奇数的话，说明已经释放了
        if (oldRef != 2 && oldRef != 4 && (oldRef & 1) != 0) {
            throw new IllegalReferenceCountException(0, increment);
        }
        // don't pass 0!
        //经过就说明有溢出了，要处理掉

        if ((oldRef <= 0 && oldRef + rawIncrement >= 0) //比如setRefCnt的时候设置了负数进去，oldRef =-1173741824，increment=1003741824 rawIncrement=2007483648
                || (oldRef >= 0 && oldRef + rawIncrement < oldRef)) { //比如setRefCnt的时候设置了正数进去，oldRef =2，increment=1103741824 rawIncrement=-2087483648
            // overflow case
            //改回来
            updater().getAndAdd(instance, -rawIncrement);
            throw new IllegalReferenceCountException(realRefCnt(oldRef), increment);
        }
        return instance;
    }

    //释放的时候会先调用nonVolatileRawCnt获得引用计数，然后判断引用计数是否是2或者减的值就是真实引用计数值，
    // 是的话就可以尝试直接设置的方法tryFinalRelease0，如果失败会去尝试释放方法retryRelease0，这个是自旋，直到成功为止。
    // 如果不是的话就普通的引用计数器值的修改即可nonFinalRelease0。当然这里可能会对引用已经是1的再进行释放，
    // 这样就会在retryRelease0中的toLiveRealRefCnt检测报异常，避免了重复释放，而且里面修改值都是原子操作，线程安全的。
    //减少计数1，返回是否真正释放
    public final boolean release(T instance) {
        //获取引用计数 如果引用计数rawCnt == 2 说明真实计数是1，就可以直接尝试最终释放，否则就真实计数减1，这个就算已经释放也不会报错
        int rawCnt = nonVolatileRawCnt(instance);
        return rawCnt == 2 ? tryFinalRelease0(instance, 2) || retryRelease0(instance, 1)
                : nonFinalRelease0(instance, 1, rawCnt, toLiveRealRefCnt(rawCnt, 1));
    }

    //减少计数decrement，返回是否真正释放
    public final boolean release(T instance, int decrement) {
        int rawCnt = nonVolatileRawCnt(instance);
        int realCnt = toLiveRealRefCnt(rawCnt, checkPositive(decrement, "decrement"));
        return decrement == realCnt ? tryFinalRelease0(instance, rawCnt) || retryRelease0(instance, decrement)
                : nonFinalRelease0(instance, decrement, rawCnt, realCnt);
    }

    private boolean tryFinalRelease0(T instance, int expectRawCnt) {
        return updater().compareAndSet(instance, expectRawCnt, 1); // any odd number will work
    }

    private boolean nonFinalRelease0(T instance, int decrement, int rawCnt, int realCnt) {
        if (decrement < realCnt
                // all changes to the raw count are 2x the "real" change - overflow is OK
                && updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
            return false;
        }
        return retryRelease0(instance, decrement);
    }

    private boolean retryRelease0(T instance, int decrement) {
        for (;;) {
            int rawCnt = updater().get(instance), realCnt = toLiveRealRefCnt(rawCnt, decrement);
            if (decrement == realCnt) {
                if (tryFinalRelease0(instance, rawCnt)) {
                    return true;
                }
            } else if (decrement < realCnt) {
                // all changes to the raw count are 2x the "real" change
                if (updater().compareAndSet(instance, rawCnt, rawCnt - (decrement << 1))) {
                    return false;
                }
            } else {
                throw new IllegalReferenceCountException(realCnt, -decrement);
            }
            Thread.yield(); // this benefits throughput under high contention
        }
    }
}
